#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h> // 添加此头文件
#include <linux/delay.h>
#include <linux/uaccess.h>

#define SET_RESOLUTION _IOW('A', 0, int)
#define READ_RESOLUTION _IOR('A', 1, int)

#define SET_RESOLUTION_9 9
#define SET_RESOLUTION_10 10
#define SET_RESOLUTION_11 11
#define SET_RESOLUTION_12 12

struct ds18b20_data
{
    dev_t dev_num;
    struct cdev ds18b20_cdev;
    struct class *ds18b20_class;
    struct device *ds18b20_device;
    struct gpio_desc *ds18b20_gpio;
};

struct ds18b20_data *ds18b20;

void ds18b20_reset(void)
{
    // 设置 GPIO 方向为输出，输出低电平
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);
    gpiod_set_value(ds18b20->ds18b20_gpio, 0);
    udelay(700); // 延迟 700 微秒

    // 设置 GPIO 输出高电平，并将 GPIO 方向设置为输入
    gpiod_set_value(ds18b20->ds18b20_gpio, 1);
    gpiod_direction_input(ds18b20->ds18b20_gpio);

    // 等待直到 GPIO 输入为低电平
    while (gpiod_get_value(ds18b20->ds18b20_gpio))
        ;

    // 等待直到 GPIO 输入为高电平
    while (!gpiod_get_value(ds18b20->ds18b20_gpio))
        ;
    udelay(480); // 延迟 480 微秒
}

/**
 * 向 DS18B20 写入单个位(bit)
 * @param bit 要写入的位(bit)，0 或 1
 */
void ds18b20_writebit(unsigned char bit) {
    // 将 GPIO 方向设置为输出
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);

    // 将 GPIO 输出设置为指定的位(bit)
    gpiod_set_value(ds18b20->ds18b20_gpio, 0);

    // 若 bit 为 1，则延时 10 微秒
    if (bit){
        udelay(10);  
        // 将 GPIO 方向设置为输出
    	gpiod_direction_output(ds18b20->ds18b20_gpio, 1);
    }
       

    // 延时 65 微秒
    udelay(65);

    // 将 GPIO 方向设置为输出
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);

    // 延时 2 微秒
    udelay(2);
}

/**
 * 向 DS18B20 写入一个字节(byte)数据
 * @param data 要写入的字节数据
 */
void ds18b20_writebyte(int data) {
    int i;

    for (i = 0; i < 8; i++) {
        // 逐位写入数据
        ds18b20_writebit(data & 0x01);
        data = data >> 1;
    }
}

/**
 * 从 DS18B20 读取单个位(bit)
 * @return 读取到的位(bit)，0 或 1
 */
unsigned char ds18b20_readbit(void) {
    unsigned char bit;        
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        
    gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        
    udelay(2);// 延时 2 微秒        
    gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   
    udelay(10);// 延时 10 微秒       
    bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       
    udelay(60);// 延时 60 微秒
    
    return bit;
}

/**
 * 从 DS18B20 读取一个字节(byte)数据
 * @return 读取到的字节数据
 */
int ds18b20_readbyte(void) {
    int data = 0;
    int i;
    
    for (i = 0; i < 8; i++) {
        // 读取单个位(bit)并根据位的位置进行左移操作
        data |= ds18b20_readbit() << i;
    }
    
    return data;
}

/**
 * 从 DS18B20 读取温度值
 * @return 读取到的温度值
 */
int ds18b20_readtemp(void) {
    int temp_l, temp_h, temp;   
    
    ds18b20_reset();// 复位 DS18B20    
    ds18b20_writebyte(0xCC);// 发送写入字节命令 0xCC（跳过 ROM）   
    ds18b20_writebyte(0x44);// 发送写入字节命令 0x44（启动温度转换）    
    mdelay(750);// 延时 750 微秒，等待温度转换完成
    
    ds18b20_reset();// 复位 DS18B20        
    ds18b20_writebyte(0xCC);// 发送写入字节命令 0xCC（跳过 ROM）       
    ds18b20_writebyte(0xBE);// 发送写入字节命令 0xBE（读取温度值）        
    temp_l = ds18b20_readbyte();// 读取温度低位字节        
    temp_h = ds18b20_readbyte();// 读取温度高位字节        
    temp_h = temp_h << 8;// 将温度高位字节左移 8 位
    temp = temp_h | temp_l;// 组合温度值
    
    return temp;
} 

/**
 * 设置 DS18B20 温度传感器的分辨率
 * @param args 分辨率参数
 */
void set_resolution(int args) {
    ds18b20_reset();  // 复位 DS18B20 温度传感器
    ds18b20_writebyte(0xCC);  // 发送跳过 ROM 命令
    ds18b20_writebyte(0x4E);  // 发送写配置寄存器命令
    ds18b20_writebyte(60);  // 发送配置字节 1，设置温度上限阈值
    ds18b20_writebyte(10);  // 发送配置字节 2，设置温度下限阈值

    switch (args) {
        case SET_RESOLUTION_9:  // 设置分辨率为 9 位
            ds18b20_writebyte(0x1F);  // 发送配置字节 3，设置分辨率为 9 位
            break;
        case SET_RESOLUTION_10:  // 设置分辨率为 10 位
            ds18b20_writebyte(0x3F);  // 发送配置字节 3，设置分辨率为 10 位
            break;
        case SET_RESOLUTION_11:  // 设置分辨率为 11 位
            ds18b20_writebyte(0x5F);  // 发送配置字节 3，设置分辨率为 11 位
            break;
        case SET_RESOLUTION_12:  // 设置分辨率为 12 位
            ds18b20_writebyte(0x7F);  // 发送配置字节 3，设置分辨率为 12 位
            break;
        default:
            break;
    }
}

/**
 * 检查参数的有效性
 * @param args 参数
 * @return 返回执行结果，成功返回 0，失败返回 -1
 */
int check_args(int args) {
    int ret = -1;  // 返回值，默认为失败

    ds18b20_reset();  // 复位传感器
    ds18b20_writebyte(0xCC);  // 发送指令字节 0xCC
    ds18b20_writebyte(0xBE);  // 发送指令字节 0xBE
    ds18b20_readbyte();  // 读取一个字节
    ds18b20_readbyte();  // 读取一个字节
    ds18b20_readbyte();  // 读取一个字节
    ds18b20_readbyte();  // 读取一个字节

    switch (args) {
        case SET_RESOLUTION_9:
            if (ds18b20_readbyte() == 0x1F) {  // 读取一个字节并与 0x1F 进行比较
                ret = 0;  // 设置返回值为成功
            }
            break;
        case SET_RESOLUTION_10:
            if (ds18b20_readbyte() == 0x3F) {  // 读取一个字节并与 0x3F 进行比较
                ret = 0;  // 设置返回值为成功
            }
            break;
        case SET_RESOLUTION_11:
            if (ds18b20_readbyte() == 0x5F) {  // 读取一个字节并与 0x5F 进行比较
                ret = 0;  // 设置返回值为成功
            }
            break;
        case SET_RESOLUTION_12:
            if (ds18b20_readbyte() == 0x7F) {  // 读取一个字节并与 0x7F 进行比较
                ret = 0;  // 设置返回值为成功
            }
            break;
        default:
            break;
    }

    return ret;  // 返回结果
}


/**
 * 读取分辨率。
 *
 * @return 分辨率值
 */
int read_resolution(void) {
    int ret;

    // 复位传感器
    ds18b20_reset();

    // 发送指令字节0xCC，跳过ROM操作，直接与单个设备通信
    ds18b20_writebyte(0xCC);

    // 发送指令字节0xBE，读取当前设备的配置寄存器
    ds18b20_writebyte(0xBE);

    // 读取4个字节的数据，但实际上只有最后一个字节是分辨率信息
    ds18b20_readbyte();
    ds18b20_readbyte();
    ds18b20_readbyte();
    ds18b20_readbyte();

    // 读取最后一个字节，即分辨率信息
    ret = ds18b20_readbyte();

    // 返回分辨率值
    return ret;
}


int ds18b20_open(struct inode *inode, struct file *file)
{
    return 0;
}

ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs) {
    int ds18b20_temp;    
    ds18b20_temp = ds18b20_readtemp();// 从 DS18B20 读取温度值    
    // 将温度值复制到用户空间缓冲区
    if (copy_to_user(buf, &ds18b20_temp, sizeof(ds18b20_temp))) {
        return -1; // 复制失败，返回错误代码
    }
    
    return 0; // 成功读取并复制温度值
}

int ds18b20_release(struct inode *inode, struct file *file)
{
    return 0;
}

/**
 * DS18B20 温度传感器的 ioctl 函数
 * @param file 文件指针
 * @param cmd 命令
 * @param args 参数
 * @return 返回执行结果，成功返回 0，失败返回 -1
 */
long ds18b20_ioctl(struct file *file, unsigned int cmd, unsigned long args) {
    int resolution;
    if (cmd == SET_RESOLUTION) {  // 判断命令是否为设置分辨率
        if (args >= SET_RESOLUTION_9 && args <= SET_RESOLUTION_12) {  // 判断参数是否在有效的分辨率范围内
            set_resolution(args);  // 调用设置分辨率的函数
            return 0;  // 返回成功
        }
    }
    else if (cmd == READ_RESOLUTION) {
        // 读取分辨率
        resolution = read_resolution();
        // 将分辨率的值复制给用户空间的args
        if (copy_to_user((int *)args, &resolution, sizeof(resolution))) {
            // 复制失败，返回-1表示失败
            return -1;
        }
    }
    // 如果不匹配 SET_RESOLUTION 或者 args 不在有效范围内，不执行任何操作
    return -1;  // 返回失败
}

struct file_operations ds18b20_fops = {
    .open = ds18b20_open,
    .read = ds18b20_read,
    .release = ds18b20_release,
    .unlocked_ioctl = ds18b20_ioctl,
    .owner = THIS_MODULE,
};

int ds18b20_probe(struct platform_device *dev)
{
    int ret;
    printk("This is probe \n");

    // 分配内存给ds18b20_data结构体
    ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);
    if (ds18b20 == NULL)
    {
        printk("kzalloc error\n");
        ret = -ENOMEM;
        goto error_0;
    }

    // 分配字符设备号
    ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");
    if (ret < 0)
    {
        printk("alloc_chrdev_region error\n");
        ret = -EAGAIN;
        goto error_1;
    }

    // 初始化字符设备
    cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);
    ds18b20->ds18b20_cdev.owner = THIS_MODULE;
    cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);

    // 创建设备类
    ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");
    if (IS_ERR(ds18b20->ds18b20_class))
    {
        printk("class_create error\n");
        ret = PTR_ERR(ds18b20->ds18b20_class);
        goto error_2;
    }

    // 创建设备
    ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");
    if (IS_ERR(ds18b20->ds18b20_device))
    {
        printk("device_create error\n");
        ret = PTR_ERR(ds18b20->ds18b20_device);
        goto error_3;
    }

    // 获取GPIO描述符
    ds18b20->ds18b20_gpio = gpiod_get_optional(&dev->dev, "ds18b20", 0);
    if (ds18b20->ds18b20_gpio == NULL)
    {
        ret = -EBUSY;
        goto error_4;
    }

    // 设置GPIO方向为输出
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);

    return 0;

    error_4:
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);

    error_3:
    class_destroy(ds18b20->ds18b20_class);

    error_2:
    cdev_del(&ds18b20->ds18b20_cdev);
    unregister_chrdev_region(ds18b20->dev_num, 1);

    error_1:
    kfree(ds18b20);

    error_0:
    return ret;
}

const struct of_device_id ds18b20_match_table[] = {
    {.compatible = "ds18b20"},
    {},
};

struct platform_driver ds18b20_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "ds18b20",
        .of_match_table = ds18b20_match_table,
    },
    .probe = ds18b20_probe,
};

static int __init ds18b20_init(void)
{
    int ret;

    // 注册平台驱动
    ret = platform_driver_register(&ds18b20_driver);
    if (ret < 0)
    {
        printk("platform_driver_register error\n");
        return -1;
    }

    return 0;
}

static void __exit ds18b20_exit(void)
{
    // 释放资源
    gpiod_put(ds18b20->ds18b20_gpio);
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);
    class_destroy(ds18b20->ds18b20_class);
    cdev_del(&ds18b20->ds18b20_cdev);
    unregister_chrdev_region(ds18b20->dev_num, 1);
    kfree(ds18b20);
    platform_driver_unregister(&ds18b20_driver);
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");
